using Godot;
using PerformanceCompareTest.Helpers;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;

public class LineInfo
{
    public double Seconds { get; set; }
    public string Content { get; set; }

    public LineInfo()
    {

    }

    public LineInfo(double seconds, string content)
    {
        Seconds = seconds;
        Content = content;
    }
}

public class LyricInfo
{
    public LineInfo[] Lines { get; set; }

    public LyricInfo(string filePath)
    {
        var lyrics = SharpLyrics.LyricReader.GetLyrics(filePath);
        var lines = new List<LineInfo>();
        foreach (var line in lyrics.Lines)
        {
            lines.Add(new LineInfo(line.LineSpan.TotalSeconds, line.Line));
        }

        Lines = lines.ToArray();
    }
}

public class LineInfoLabelWrapper
{
    #region fields

    private LineInfo _info;

    #endregion


    #region props

    public Label Label => _label;
    private Label _label;

    public double StartTime { get; set; }
    public double EndTime { get; set; }

    public bool Visible { get => _visible; set => SetVisible(value); }
    private bool _visible = true;

    public Vector2 Position { get => _position; set => SetPosition(value); }
    private Vector2 _position = Vector2.Down;

    public Vector2 TempPosition;

    #endregion


    public LineInfoLabelWrapper(Label label, LineInfo info)
    {
        _label = label;
        _info = info;

    }

    #region methods

    private void SetVisible(bool value)
    {
        if (_visible == value) return;
        _visible = value;
        _label.Visible = value;
    }

    private void SetPosition(Vector2 value)
    {
        if (_position == value) return;
        _position = value;
        _label.Position = value;
    }

    public void UpdateTempPosition()
    {
        TempPosition = Position;
    }

    public void ApplyTempPosition()
    {
        Position = TempPosition;
    }

    #endregion
}

public class LyricVisibleIndexRange
{
    public int StartIndex;
    public int EndIndex;
    public int FocusIndex;

    public override string ToString()
    {
        return $"start {StartIndex} end {EndIndex} focus {FocusIndex}";
    }
}

public class LyricManager
{
    public const float LineHeight = 30;

    public List<LineInfoLabelWrapper> Lines;

    // 偏移时间。
    public double OffsetTime;

    private int _tempRangeIndex = -1;

    private double _lastTimeMovementProgress = -1;

    public LyricManager(LyricInfo lyric)
    {
        Init(lyric);
    }

    ///<summary>初始化歌词信息。</summary>
    private void Init(LyricInfo lyric)
    {
        var actualTime = 0 + OffsetTime;
        Lines = new List<LineInfoLabelWrapper>();
        var len = lyric.Lines.Length;
        for (int i = 0; i < len; i++)
        {
            var lineInfo = lyric.Lines[i];
            var wrapper = CreateLineInfoLabelWrapper(lineInfo);
            wrapper.StartTime = lineInfo.Seconds;

            if (i == len - 1)
            {
                wrapper.EndTime = lineInfo.Seconds + 1;

            }
            else
            {
                var nextInfo = lyric.Lines[i + 1];
                wrapper.EndTime = nextInfo.Seconds;
            }

            wrapper.Visible = false;
            Lines.Add(wrapper);
        }
    }

    public void Update(double time)
    {
        var startTime = DateTime.Now;
        // 获得当前可见范围内的视图，从上三个到下三个共七个。
        var indexRange = GetIndexRange(time);

        if (indexRange.FocusIndex < 0) return;

        UpdateInit();

        UpdateVisible(indexRange);

        var timeMovementProgress = GetTimeMovementProgress(indexRange, time);

        if (_tempRangeIndex == indexRange.FocusIndex && _lastTimeMovementProgress == timeMovementProgress)
        {
            // DONOTHING
        }
        else
        {
            UpdateMovement(indexRange, timeMovementProgress);
            MoveFocusToCenter(indexRange, timeMovementProgress);
            UpdateEffect(indexRange, timeMovementProgress);
            ApplyUpdate();
        }



        var endTime = DateTime.Now;

        GD.Print($"{(endTime - startTime).TotalSeconds: 0.#################}");

    }

    private void UpdateInit()
    {
        foreach (var line in Lines)
        {
            line.UpdateTempPosition();
        }
    }

    private void ApplyUpdate()
    {
        foreach (var line in Lines)
        {
            line.ApplyTempPosition();
        }
    }


    private void UpdateVisible(LyricVisibleIndexRange indexRange)
    {
        for (int i = 0; i < indexRange.StartIndex; i++)
        {
            Lines[i].Visible = false;
        }

        for (int i = indexRange.EndIndex + 1; i < Lines.Count; i++)
        {
            Lines[i].Visible = false;
        }

        for (int i = indexRange.StartIndex; i <= indexRange.EndIndex; i++)
        {
            Lines[i].Visible = true;
        }
    }

    private void UpdateMovement(LyricVisibleIndexRange indexRange, double timeMovementProgress)
    {
        for (int i = indexRange.StartIndex; i <= indexRange.EndIndex; i++)
        {
            var line = Lines[i];
            var position = GetMovementPosition(i, timeMovementProgress);
            line.TempPosition = position;

        }
    }

    private double GetTimeMovementProgress(LyricVisibleIndexRange indexRange, double time)
    {
        var focusLine = Lines[indexRange.FocusIndex];
        var focusStartTime = focusLine.StartTime;
        var focusEndTime = focusLine.EndTime;

        var timeMovementProgress = TimeInMovementPartProgress(focusStartTime, focusEndTime, time);
        return timeMovementProgress;
    }

    private void MoveFocusToCenter(LyricVisibleIndexRange indexRange, double timeMovementProgress)
    {
        var focusLine = Lines[indexRange.FocusIndex];

        var focusPosition = new Vector2(0, (float)(1 - timeMovementProgress) * LineHeight);
        var relVector = focusPosition - focusLine.TempPosition;

        for (int i = indexRange.StartIndex; i <= indexRange.EndIndex; i++)
        {
            var line = Lines[i];
            var position = GetMovementPosition(i, timeMovementProgress);
            line.TempPosition += relVector;
        }
    }

    private void UpdateEffect(LyricVisibleIndexRange indexRange, double timeMovementProgress)
    {
        var focusLine = Lines[indexRange.FocusIndex];

        for (int i = indexRange.StartIndex; i <= indexRange.EndIndex; i++)
        {
            var deltaTween = 0d;

            if (i <= indexRange.FocusIndex)
            {
                deltaTween = (Math.Abs(i - indexRange.FocusIndex) + timeMovementProgress) * 30f;
            }
            else
            {
                deltaTween = ((Math.Abs(i - indexRange.FocusIndex) - 1) + (1 - timeMovementProgress)) * 30f;
            }

            var line = Lines[i];
            line.TempPosition += Vector2.Right * (float)deltaTween;
        }
    }

    ///<summary>判断么时间是否在移动的部分，如果在移动的部分，则会传出在移动过程的百分比。</summary>
    private double TimeInMovementPartProgress(double startTime, double endTime, double time)
    {
        var movementPartProgress = 0.2;
        var progress = TimeHelper.GetProgressWithEndTime(startTime, endTime, time);

        var timeMovementProgress = Math.Min(1, progress / movementPartProgress);

        return timeMovementProgress;
    }

    private Vector2 GetMovementPosition(int index, double timeMovementProgress)
    {
        var startY = index * LineHeight;
        var movementY = (float)timeMovementProgress * LineHeight;
        var finalY = startY - movementY;
        return Vector2.Down * finalY;
    }

    ///<summary>传入指定时间，返回那些可以被纳入计算。</summary>
    private LyricVisibleIndexRange GetIndexRange(double time)
    {

        var focusIndex = GetRangeIndex(time);

        var startIndex = Math.Max(0, focusIndex - 4);

        var endIndex = Math.Min(Lines.Count - 1, focusIndex + 5);

        return new LyricVisibleIndexRange()
        {
            FocusIndex = focusIndex,
            StartIndex = startIndex,
            EndIndex = endIndex
        };
    }

    private int GetRangeIndex(double time)
    {
        if (_tempRangeIndex > 0)
        {

            var line = Lines[_tempRangeIndex];
            var isInRange = TimeHelper.IsInRange(line.StartTime, line.EndTime, time);
            if (isInRange) return _tempRangeIndex;
        }

        var result = GetRangeIndexCore(time);

        _tempRangeIndex = result;

        return result;
    }

    private int GetRangeIndexCore(double time)
    {
        for (int i = 0; i < Lines.Count; i++)
        {
            var line = Lines[i];

            var isInRange = TimeHelper.IsInRange(line.StartTime, line.EndTime, time);

            if (isInRange == true) return i;
        }
        return -1;
    }

    private LineInfoLabelWrapper CreateLineInfoLabelWrapper(LineInfo lineInfo)
    {
        var label = new Label() { Size = new Vector2(100, LineHeight) };
        label.Text = lineInfo.Content;

        return new LineInfoLabelWrapper(label, lineInfo);
    }

    private double GetActualTime(double originTime, double offsetTime)
    {
        return originTime + offsetTime;
    }

}

public partial class MainScene : Node2D
{
    private Polygon2D _polygon2D;
    private LyricManager _manager;

    public override void _Ready()
    {
        var button = GetNode<Button>("Button");
        button.Pressed += Button_Pressed;

        var slider = GetNode<HSlider>("HSlider");
        slider.ValueChanged += Slider_ValueChanged;

        GD.Print();

        _manager = new LyricManager(new LyricInfo(Path.Combine(System.Environment.CurrentDirectory, "geci.lrc")));

    }

    private void Slider_ValueChanged(double value)
    {
        _manager.Update(value * _manager.Lines.Last().EndTime);
    }

    private void Button_Pressed()
    {
        _manager.Update(0);
        var lyricContainer = GetNode<Node2D>($"LyricContainer");
        foreach (var line in _manager.Lines)
        {
            var label = line.Label;
            lyricContainer.AddChild(label);
        }

    }

    public override void _Process(double delta)
    {

    }
}
